﻿namespace Windows32
{
    using System;
    using System.Drawing;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    using XDesignerCommon;


    [StructLayout(LayoutKind.Sequential)]
    public struct NativeMessage
    {
        public IntPtr handle;
        public uint message;
        public IntPtr wParam;
        public IntPtr lParam;
        public uint time;
        public System.Drawing.Point p;
    }

    public class MouseCapturer
    {
        private ReversibleShapeStyle intReversibleShape;
        protected Control myBindControl;
        private Rectangle myClipRectangle;
        private Point myCurrentPosition;
        private Point myEndPosition;
        private Point myInitStartPosition;
        private Point myLastPosition;
        private Size myMoveSize;
        private Point myStartPosition;
        private object objTag;

        public event CaptureMouseMoveEventHandler Draw;

        public event CaptureMouseMoveEventHandler MouseMove;

        public MouseCapturer()
        {
            this.myBindControl = null;
            this.myInitStartPosition = Point.Empty;
            this.myStartPosition = Point.Empty;
            this.myEndPosition = Point.Empty;
            this.myLastPosition = Point.Empty;
            this.myCurrentPosition = Point.Empty;
            this.myMoveSize = Size.Empty;
            this.myClipRectangle = Rectangle.Empty;
            this.MouseMove = null;
            this.Draw = null;
            this.intReversibleShape = ReversibleShapeStyle.Custom;
            this.objTag = null;
        }

        public MouseCapturer(Control ctl)
        {
            this.myBindControl = null;
            this.myInitStartPosition = Point.Empty;
            this.myStartPosition = Point.Empty;
            this.myEndPosition = Point.Empty;
            this.myLastPosition = Point.Empty;
            this.myCurrentPosition = Point.Empty;
            this.myMoveSize = Size.Empty;
            this.myClipRectangle = Rectangle.Empty;
            this.MouseMove = null;
            this.Draw = null;
            this.intReversibleShape = ReversibleShapeStyle.Custom;
            this.objTag = null;
            this.myBindControl = ctl;
        }

        public bool CaptureMouseMove()
        {
            this.Reset();
            int i = 0;
            NativeMessage msg = new NativeMessage();
            int MinDragSize = SystemInformation.DragSize.Width;
            bool DragStartFlag = false;
            if (Control.MouseButtons == MouseButtons.None)
            {
                return false;
            }
            Point curPoint = this.CurMousePosition;
            while (WaitMessage())
            {
                i++;
                curPoint = this.CurMousePosition;
                if (Control.MouseButtons == MouseButtons.None)
                {
                    break;
                }
                if (PeekMessage(ref msg, IntPtr.Zero, 0, 0, 0))
                {
                    if (isMouseUpMessage((int)msg.message))
                    {
                        curPoint.X = (short)msg.lParam;
                        curPoint.Y = (int)msg.lParam >> 0x10;
                        break;
                    }
                    if (isMouseMoveMessage((int)msg.message))
                    {
                        Point p = new Point((short)msg.lParam, (int)msg.lParam >> 0x10);
                        if ((p.X != this.myCurrentPosition.X) || (p.Y != this.myCurrentPosition.Y))
                        {
                            if (DragStartFlag)
                            {
                                this.OnDraw();
                            }
                            this.myCurrentPosition = p;
                            if (!DragStartFlag && ((Math.Abs((int)(this.myCurrentPosition.X - this.myStartPosition.X)) >= MinDragSize) || (Math.Abs((int)(this.myCurrentPosition.Y - this.myStartPosition.Y)) >= MinDragSize)))
                            {
                                DragStartFlag = true;
                            }
                            if (DragStartFlag)
                            {
                                this.myCurrentPosition = p;
                                this.OnDraw();
                                this.OnMouseMove();
                                this.myLastPosition = this.myCurrentPosition;
                            }
                        }
                    }
                    GetMessage(ref msg, 0, 0, 0);
                }
            }
            this.myCurrentPosition = curPoint;
            if (DragStartFlag)
            {
                this.OnDraw();
            }
            this.myEndPosition = this.myCurrentPosition;
            this.myMoveSize = new Size(this.myEndPosition.X - this.myStartPosition.X, this.myEndPosition.Y - this.myStartPosition.Y);
            if ((this.myMoveSize.Width == 0) && (this.myMoveSize.Height == 0))
            {
                return false;
            }
            return DragStartFlag;
        }

        protected virtual CaptureMouseMoveEventArgs CreateArgs()
        {
            return new CaptureMouseMoveEventArgs(this, this.myStartPosition, this.myCurrentPosition);
        }

        private Point FixPointForControl(Point p)
        {
            if (this.myBindControl != null)
            {
                p = this.myBindControl.PointToClient(p);
            }
            return p;
        }

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        private static extern bool GetMessage(ref NativeMessage msg, int hWnd, uint wFilterMin, uint wFilterMax);
        private Point GetMousePosition(Point p)
        {
            if (this.myBindControl != null)
            {
                p = this.myBindControl.PointToClient(p);
            }
            return RectangleCommon.MoveInto(p, this.myClipRectangle);
        }

        private static bool isMouseMoveMessage(int intMessage)
        {
            return ((intMessage == 0x200) || (intMessage == 160));
        }

        private static bool isMouseUpMessage(int intMessage)
        {
            return ((((intMessage == 0x202) || (intMessage == 520)) || ((intMessage == 0x205) || (intMessage == 0x20c))) || (((intMessage == 0xa2) || (intMessage == 0xa8)) || ((intMessage == 0xa5) || (intMessage == 0xac))));
        }

        protected virtual void OnDraw()
        {
            //if (this.Draw != null)
            //{
            //    this.Draw(this, this.CreateArgs());
            //}
        }

        protected virtual void OnMouseMove()
        {
            if (this.MouseMove != null)
            {
                this.MouseMove(this, this.CreateArgs());
            }
        }

        protected virtual void OnReversibleDrawCallback()
        {
            Rectangle rect = RectangleCommon.GetRectangle(this.myStartPosition, this.myCurrentPosition);
            switch (this.intReversibleShape)
            {
                case ReversibleShapeStyle.Line:
                    ControlPaint.DrawReversibleLine(this.myStartPosition, this.myCurrentPosition, Color.Black);
                    break;

                case ReversibleShapeStyle.Rectangle:
                    ControlPaint.DrawReversibleFrame(rect, Color.SkyBlue, FrameStyle.Dashed);
                    break;

                case ReversibleShapeStyle.FillRectangle:
                    ControlPaint.FillReversibleRectangle(rect, Color.Black);
                    break;

                case ReversibleShapeStyle.Custom:
                    if (this.Draw != null)
                    {
                        this.Draw(this, null);
                    }
                    break;
            }
        }

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        private static extern bool PeekMessage(ref NativeMessage msg, IntPtr hWnd, uint wFilterMin, uint wFilterMax, uint wFlag);
        public void Reset()
        {
            if (this.myInitStartPosition.IsEmpty)
            {
                this.myStartPosition = this.CurMousePosition;
            }
            else
            {
                this.myStartPosition = this.myInitStartPosition;
            }
            this.myLastPosition = this.myStartPosition;
            this.myCurrentPosition = this.myStartPosition;
            this.myEndPosition = this.myStartPosition;
            this.myMoveSize = Size.Empty;
        }

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        private static extern bool WaitMessage();

        public Control BindControl
        {
            get
            {
                return this.myBindControl;
            }
            set
            {
                this.myBindControl = value;
            }
        }

        public Rectangle CaptureRectagle
        {
            get
            {
                Rectangle rect = RectangleCommon.GetRectangle(this.myStartPosition, this.myEndPosition);
                rect.Location = this.FixPointForControl(rect.Location);
                return rect;
            }
        }

        public Rectangle ClipRectangle
        {
            get
            {
                return this.myClipRectangle;
            }
            set
            {
                this.myClipRectangle = value;
            }
        }

        private Point CurMousePosition
        {
            get
            {
                return this.GetMousePosition(Control.MousePosition);
            }
        }

        public Size CurrentMoveSize
        {
            get
            {
                return new Size(this.myCurrentPosition.X - this.myStartPosition.X, this.myCurrentPosition.Y - this.myStartPosition.Y);
            }
        }

        public Point CurrentPosition
        {
            get
            {
                return this.myCurrentPosition;
            }
        }

        public int DX
        {
            get
            {
                return (this.myEndPosition.X - this.myStartPosition.X);
            }
        }

        public int DY
        {
            get
            {
                return (this.myEndPosition.Y - this.myStartPosition.Y);
            }
        }

        public Point EndPosition
        {
            get
            {
                return this.myEndPosition;
            }
        }

        public Point InitStartPosition
        {
            get
            {
                return this.myInitStartPosition;
            }
            set
            {
                this.myInitStartPosition = value;
            }
        }

        public Point LastPosition
        {
            get
            {
                return this.myLastPosition;
            }
        }

        public Size MoveSize
        {
            get
            {
                return this.myMoveSize;
            }
            set
            {
                this.myMoveSize = value;
            }
        }

        public ReversibleShapeStyle ReversibleShape
        {
            get
            {
                return this.intReversibleShape;
            }
            set
            {
                this.intReversibleShape = value;
            }
        }

        public Point StartPosition
        {
            get
            {
                return this.myStartPosition;
            }
        }

        public object Tag
        {
            get
            {
                return this.objTag;
            }
            set
            {
                this.objTag = value;
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct MSG
        {
            public int hwnd;
            public int message;
            public int wParam;
            public int lParam;
            public int time;
            public int pt_x;
            public int pt_y;
        }
    }
}

